package com.yk.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yk.api.system.dto.AlarmHistoryDTO;
import com.yk.common.core.constant.NumberConstant;
import com.yk.common.core.dto.SendWechatDTO;
import com.yk.common.core.utils.DateUtils;
import com.yk.common.core.utils.SendUtils;
import com.yk.system.convert.AlarmHistoryConvert;
import com.yk.system.entity.*;
import com.yk.system.mapper.AlarmHistoryMapper;
import com.yk.system.mapper.AlarmUserDeviceMapper;
import com.yk.system.service.*;
import com.yk.system.vo.AlarmDeviceStatisticsChartsVO;
import com.yk.system.vo.AlarmDeviceStatisticsVO;
import com.yk.system.vo.AlarmGroupStatisticsVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 报警历史表 服务类
 *
 * @author lmx
 * @since 2023-11-21
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AlarmHistoryServiceImpl extends ServiceImpl<AlarmHistoryMapper, AlarmHistory> implements AlarmHistoryService {

    private final SendUtils sendUtils;
    private final AlarmHistoryConvert alarmHistoryConvert;
    private final DeviceService deviceService;
    private final UserService userService;
    private final UserWxService userWxService;
    private final DeviceRoleService deviceRoleService;
    private final AlarmUserDeviceMapper alarmUserDeviceMapper;
    private final GroupDeviceService groupDeviceService;
    private final VariableService variableService;
    @Value("${sms.alibaba.alarmTemplateId}")
    private String templateId;

    @Override
    public void dealWithAlarmMessage(AlarmHistoryDTO dto) {
        Long deviceId = dto.getDeviceId();
        Long variableId = dto.getVariableId();
        Boolean alarmState = dto.getAlarmState();
        Long alarmId = dto.getAlarmId();
        LambdaQueryWrapper<AlarmHistory> lambda = new LambdaQueryWrapper<>();
        lambda.eq(AlarmHistory::getDeviceId, deviceId);
        lambda.eq(AlarmHistory::getVariableId, variableId);
        lambda.eq(AlarmHistory::getAlarmId, alarmId);
        lambda.eq(AlarmHistory::getAlarmState, Boolean.TRUE);
        AlarmHistory toUpdate = baseMapper.selectOne(lambda);
        AlarmHistory toSave = alarmHistoryConvert.dto2Entity(dto);
        // 正常且无报警记录、未触发报警
        if (!alarmState && Objects.isNull(toUpdate)) {
            return;
        }
        // 报警且有报警记录、无需触发报警
        if (alarmState && Objects.nonNull(toUpdate)) {
            return;
        }
        if (!alarmState) {
            // 报警恢复
            toUpdate.setAlarmState(Boolean.FALSE);
            toUpdate.setRecoverValue(dto.getRecoverValue());
            toUpdate.setRecoverAt(new Date());
            baseMapper.updateById(toUpdate);
        } else {
            // 开始报警
            toSave.setAlarmState(Boolean.TRUE);
            toSave.setAlarmAt(new Date());
            baseMapper.insert(toSave);
            // 获取设备管理人
            List<Long> userIds = deviceRoleService.selectByDeviceIdAdmin(deviceId);
            if (CollUtil.isEmpty(userIds)) {
                return;
            }
            String informType = dto.getInformType();
            for (Long userId : userIds) {
                AlarmUserDevice alarmUserDevice = alarmUserDeviceMapper.selectByUserAndDevice(userId,
                        toSave.getDeviceId(), toSave.getVariableId(), toSave.getAlarmId());
                // 如果用户单独配置了通知方式，则使用用户配置的
                if (Objects.nonNull(alarmUserDevice)) {
                    if (StrUtil.isEmpty(alarmUserDevice.getInformType())){
                        continue;
                    }
                    informType = alarmUserDevice.getInformType();
                }
                String[] split = informType.split(",");
                for (String s : split) {
                    if (Objects.equals(s, NumberConstant.ZERO_STR)) {
                        gzhSendAlarm(userId, deviceId, dto.getMessage(), dto.getAlarmValue());
                        log.info("公众号报警信息推送, {}", dto.getMessage());
                    } else if (Objects.equals(s, NumberConstant.ONE_STR)) {
                        sendAlibabaSms(userId, deviceId, dto.getMessage());
                    }
                }
            }
        }
    }

    /**
     * 公众号报警信息推送
     *
     * @param userId     设备管理人员Id
     * @param deviceId   设备Id
     * @param message    报警信息
     * @param alarmValue 报警值
     */
    private void gzhSendAlarm(Long userId, Long deviceId, String message, String alarmValue) {
        SendWechatDTO sendWechatDTO = new SendWechatDTO();
        sendWechatDTO.setType(NumberConstant.THREE_STR);
        // 设备名称
        Device device = deviceService.getById(deviceId);
        if (Objects.isNull(device)) {
            log.error("报警失败，设备信息不存在：{}", deviceId);
            return;
        }
        String name = device.getDeviceName();
        sendWechatDTO.setName(name);
        // 告警原因
        sendWechatDTO.setMsg(message);
        // 告警值
        sendWechatDTO.setValue(alarmValue);
        UserWx userWx = userWxService.getOne(new LambdaQueryWrapper<UserWx>().eq(UserWx::getUserId, userId));
        if (Objects.nonNull(userWx) && CharSequenceUtil.isNotEmpty(userWx.getGOpenId())) {
            sendWechatDTO.setOpenId(userWx.getGOpenId());
            sendUtils.sendWechatMessage(sendWechatDTO);
        }
    }

    /**
     * 给设备管理员发短信
     *
     * @param userId   设备管理人员Id
     * @param deviceId 设备Id
     * @param message  报警信息
     */
    private void sendAlibabaSms(Long userId, Long deviceId, String message) {
        // 设备名称
        Device device = deviceService.getById(deviceId);
        if (Objects.isNull(device)) {
            log.error("报警失败，设备信息不存在：{}", deviceId);
            return;
        }
        LinkedHashMap<String, String> map = new LinkedHashMap<>(2);
        User user = userService.getById(userId);
        if (Objects.nonNull(user) && CharSequenceUtil.isNotEmpty(user.getPhone())) {
            map.put("device", device.getDeviceName());
            map.put("content", message);
            sendUtils.sendAlibabaAlarmSms(user.getPhone(), map, templateId);
        }
    }

    @Override
    public List<AlarmDeviceStatisticsVO> statisticsByDevice(AlarmHistoryDTO dto) {
        List<String> dateStrList = DateUtils.getDataListByDay(dto.getStartTime(), dto.getEndTime());
        List<AlarmDeviceStatisticsVO> list = baseMapper.statisticsByDevice(dto);
        List<AlarmDeviceStatisticsVO> returnList = CollUtil.newArrayList();
        if (CollUtil.isNotEmpty(list)) {
            // 根据time转成map
            Map<String, AlarmDeviceStatisticsVO> map = list.stream().collect(Collectors.toMap(AlarmDeviceStatisticsVO::getTime, it -> it));
            dateStrList.forEach(it -> {
                AlarmDeviceStatisticsVO vo = map.get(it);
                if (Objects.isNull(vo)) {
                    returnList.add(new AlarmDeviceStatisticsVO(it, 0, 0));
                } else {
                    returnList.add(vo);
                }
            });
        }
        return returnList;
    }

    @Override
    public List<AlarmDeviceStatisticsVO> statisticsHourByDevice(AlarmHistoryDTO dto) {
        if (CollUtil.isEmpty(dto.getDeviceIdList())) {
            return CollUtil.newArrayList();
        }
        Date startTime = DateUtils.getDayStartTime(dto.getStartTime());
        Date endTime = DateUtils.getDayEndTime(dto.getStartTime());
        List<String> dateStrList = DateUtils.getHours(startTime, endTime);
        List<AlarmDeviceStatisticsVO> list = baseMapper.statisticsHourByDevice(dto);
        List<AlarmDeviceStatisticsVO> returnList = CollUtil.newArrayList();
        if (CollUtil.isNotEmpty(list)) {
            // 根据time转成map
            Map<String, AlarmDeviceStatisticsVO> map = list.stream().collect(Collectors.toMap(AlarmDeviceStatisticsVO::getTime, it -> it));
            dateStrList.forEach(it -> {
                AlarmDeviceStatisticsVO vo = map.get(it);
                if (Objects.isNull(vo)) {
                    returnList.add(new AlarmDeviceStatisticsVO(it, 0, 0));
                } else {
                    returnList.add(vo);
                }
            });
        }
        return returnList;
    }

    @Override
    public List<AlarmDeviceStatisticsChartsVO> statisticsByDevice2Charts(AlarmHistoryDTO dto) {
        if (CollUtil.isEmpty(dto.getDeviceIdList())) {
            return CollUtil.newArrayList();
        }
        List<AlarmDeviceStatisticsChartsVO> list = baseMapper.statisticsByDevice2Charts(dto);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        int sum = list.stream().mapToInt(AlarmDeviceStatisticsChartsVO::getAlarmCount).sum();
        BigDecimal dCount = new BigDecimal(String.valueOf(sum));
        list.forEach(it -> {
            it.setCount(dCount.intValue());
            Device device = deviceService.getById(it.getDeviceId());
            if (Objects.nonNull(device)) {
                it.setDeviceName(device.getDeviceName());
            }
            if (NumberConstant.ZERO.equals(it.getAlarmCount())) {
                it.setPercentage("0%");
                return;
            }
            BigDecimal aCount = new BigDecimal(String.valueOf(it.getAlarmCount()));
            BigDecimal percentage = aCount.divide(dCount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
            it.setPercentage(percentage.intValue() + "%");
        });
        return list;
    }

    @Override
    public List<AlarmDeviceStatisticsChartsVO> statisticsByDeviceId2Charts(AlarmHistoryDTO dto) {
        List<AlarmDeviceStatisticsChartsVO> list = baseMapper.statisticsByDeviceId2Charts(dto);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        int sum = list.stream().mapToInt(AlarmDeviceStatisticsChartsVO::getAlarmCount).sum();
        BigDecimal dCount = new BigDecimal(String.valueOf(sum));
        list.forEach(it -> {
            it.setCount(dCount.intValue());
            Variable variable = variableService.getById(it.getVariableId());
            if (Objects.nonNull(variable)) {
                it.setVariableName(variable.getName());
            }
            if (NumberConstant.ZERO.equals(it.getAlarmCount())) {
                it.setPercentage("0%");
                return;
            }
            BigDecimal aCount = new BigDecimal(String.valueOf(it.getAlarmCount()));
            BigDecimal percentage = aCount.divide(dCount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
            it.setPercentage(percentage.intValue() + "%");
        });
        return list;
    }

    @Override
    public List<AlarmDeviceStatisticsVO> app2statisticsByDevice(AlarmHistoryDTO dto) {
        if (CollUtil.isEmpty(dto.getDeviceIdList())) {
            return CollUtil.newArrayList();
        }
        List<String> dateStrList = DateUtils.getDataListByDay(dto.getStartTime(), dto.getEndTime());
        List<AlarmDeviceStatisticsVO> list = baseMapper.app2statisticsByDevice(dto);
        List<AlarmDeviceStatisticsVO> returnList = CollUtil.newArrayList();
        if (CollUtil.isNotEmpty(list)) {
            // 根据time转成map
            Map<String, AlarmDeviceStatisticsVO> map = list.stream().collect(Collectors.toMap(AlarmDeviceStatisticsVO::getTime, it -> it));
            dateStrList.forEach(it -> {
                AlarmDeviceStatisticsVO vo = map.get(it);
                if (Objects.isNull(vo)) {
                    returnList.add(new AlarmDeviceStatisticsVO(it, 0, 0));
                } else {
                    returnList.add(vo);
                }
            });
        }
        return returnList;
    }

    @Override
    public List<AlarmGroupStatisticsVO> statisticsByGroup(AlarmHistoryDTO dto) {
        List<GroupDevice> groupDevices = groupDeviceService.list(new LambdaQueryWrapper<GroupDevice>().select(GroupDevice::getDeviceId).eq(GroupDevice::getGroupId, dto.getGroupId()));
        List<AlarmGroupStatisticsVO> returnList = CollUtil.newArrayList();
        if (CollUtil.isEmpty(groupDevices)) {
            return returnList;
        }
        List<Long> deviceIdList = groupDevices.stream().map(GroupDevice::getDeviceId).collect(Collectors.toList());
        return baseMapper.statisticsByGroup(deviceIdList, dto.getStartTime(), dto.getEndTime());
    }
}